JS30 Day 8 筆記


Posted by GL on 2023-05-23

功能

1.用 HTML5 canva 做一個畫布
2.滑鼠可畫出不同顏色與粗細的線

Demo

step 1 : 建立 <canvas> 區塊

HTML

// 像素是 800 x 800 的畫布
<canvas id="draw" width="800" height="800"></canvas>

Javascript

const canvas = document.querySelector('#draw') 

// 取得 canvas 的 context,獲得一個 2d 的畫布
const ctx = canvas.getContext('2d')

// canvas 畫布的寬高,設為同視窗大小
canvas.width = window.innerWidth
canvas.height = window.innerHeight

step 2 : 設置各個變數

// 線條的顏色
ctx.strokeStyle = '#BADA55'
// 線條轉角的樣式
ctx.lineJoin = 'round'

// 線條的末端的樣式
ctx.lineCap = 'round'

// 線條的寬度
ctx.lineWidth = 100

// 圖片相疊加時的效果
// ctx.globalCompositeOperation = 'multiply'

// 建立一個變數 idDrawing,判斷是否正在畫圖
let isDrawing = false

// 畫筆起始位置,先預設為 0
let lastX = 0 
let lastY = 0

// hsl 中的色相數值,預設為 0
let hue = 0

// 建立一個變數 direction,判斷是否正在畫圖
let direction = true

step 3 : 建立函式 draw(),執行畫圖

function draw(e){
  // 如果不是 mousedown的事件,isDrawing 為 false 並 return,否則進行下面的程式
  if(!isDrawing) return

  // 設定線條的顏色(HSL 模式)
  ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`

  // 開始規劃畫圖路徑
  ctx.beginPath()

  // 設定畫筆的初始位置
  ctx.moveTo(lastX, lastY)

  // 設定畫到往哪裡
  ctx.lineTo(e.offsetX, e.offsetY);

  //結束規劃路徑
  ctx.closePath();
  // 畫出來 
  ctx.stroke();

  // 用 ES6 賦值的方法,將結束的位置更新為下一次的起始位置
  [lastX, lastY] = [e.offsetX, e.offsetY];

  // 顏色的 HSL 數值加一
  hue++

  // 為了讓顏色不斷地在 HSL 環中循環變化
  // 如果 HSL 數值大於 360, HSL 數值歸零
  if(hue >= 360){
    hue = 0
  }


  // 為了讓線條粗細不斷地在變化,做了二次判斷
  // 如果線條寬度大於或小於某數值,切換 direction
  if(ctx.lineWidth >=100 || ctx.lineWidth <= 1){
    direction = !direction
  }

  // direction 為 true, 線條粗細 + 1,反之 - 1
  if(direction){
    ctx.lineWidth++
  }else{
    ctx.lineWidth--
  }
}

step 4 : 監聽滑鼠操作的事件

// 按下滑鼠時
canvas.addEventListener('mousedown',(e)=>{
  // console.log('mousedown', e)

 // isDrawing 設為 true
 isDrawing = true

 // 將當前滑鼠的位置設為畫筆的起始位置
 [lastX, lastY] = [e.offsetX, e.offsetY]
})

// 當滑鼠移動時,執行 draw()
canvas.addEventListener('mousemove',draw)

// 當滑鼠放開時,isDrawing 設為 false
canvas.addEventListener('mouseup', ()=> isDrawing = false)

// 當滑鼠離開時,isDrawing 設為 false
canvas.addEventListener('mouseout', ()=> isDrawing = false)

 // mouseleave 可以用在結構是多層 div 時的滑鼠離開
  /*canvas.addEventListener('mouseleave', (e) => {
    // console.log('mouseleave', e);
    isDrawing = false;
  });*/

其他補充

  1. 如果要還原上一步的筆劃
    思路:可以把每一步儲存成圖片,然後抽掉步驟的圖片
    getImageData()

  2. 如果出去畫布範圍再回來

// 建立一個變數 idDrawing,判斷是否正在畫圖
let isDrawing = false

// 加這個判斷:建立一個變數 down,判斷是否正按下滑鼠
let down = false

// 按下滑鼠時
canvas.addEventListener('mousedown',(e)=>{
  // console.log('mousedown', e)

  // down 設為 true
  down = true

  // 將當前滑鼠的位置設為畫筆的起始位置
  [lastX, lastY] = [e.offsetX, e.offsetY]
})

// 當滑鼠移動時
canvas.addEventListener('mousemove',()=>{
  // 如果滑鼠沒有按下或沒有畫圖,return,否則執行 draw()
  if(!down ||!drawing) return 
  draw()
})

// 當在視窗內偵測到滑鼠放開時,isDrawing 設為 false
document.addEventListener('mouseup', ()=> isDrawing = false)

// 當滑鼠進入畫布時,isDrawing 設為 false
canvas.addEventListener('mouseleave', ()=>{
  isDrawing = true

  // 將當前滑鼠的位置設為畫筆的起始位置
  [lastX, lastY] = [e.offsetX, e.offsetY]   
})

參考資料:


#JS 30







Related Posts

部署 (1) —— 建立 AWS EC2 主機及 SSH 連線

部署 (1) —— 建立 AWS EC2 主機及 SSH 連線

Event Capture, Propagation, Bubbling and Once

Event Capture, Propagation, Bubbling and Once

W24_前端框架 redux middleware 以及 SSR、Next.js、TypeScript

W24_前端框架 redux middleware 以及 SSR、Next.js、TypeScript


Comments